﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using DG.Common.EF;
using DG.Common.EF.WPF;
using DG.Common.Patterns.Commands;
using DG.Common.Patterns.MVVM;
using TCC.ControleFluxoDeCaixa.Apresentacao.ViewModels.Credito;
using TCC.ControleFluxoDeCaixa.Apresentacao.ViewModels.DashBoard;
using TCC.ControleFluxoDeCaixa.Apresentacao.Views;
using TCC.ControleFluxoDeCaixa.Model.Contas;
using TCC.ControleFluxoDeCaixa.Model.Entities;
using TCC.ControleFluxoDeCaixa.Model.Fluxo;
using TCC.ControleFluxoDeCaixa.Model.Fluxo.Credito;
using TCC.ControleFluxoDeCaixa.Model.Fluxo.TranferenciaEntreContas;
using TCC.ControleFluxoDeCaixa.Model.Gerenciadores;
using TCC.ControleFluxoDeCaixa.Model.Usuarios;
using Xceed.Wpf.Toolkit.Zoombox;

namespace TCC.ControleFluxoDeCaixa.Apresentacao.ViewModels
{
    public class TelaPrincipalViewModel : ViewModelBase
    {
        private FluxoContextFactory _contextFactory = FluxoContextFactory.GetInstance();
        private AutenticadorDeSessao _loginAuthenticator = AutenticadorDeSessao.GetInstance();
        private DashBoardViewModel _dashBoard;

        public TelaPrincipalViewModel()
        {
            _dashBoard = new DashBoardViewModel();
            _loginAuthenticator.PropertyChanged += (sender, args) =>
            {
                DashBoard = new DashBoardViewModel();
                DashBoard.MessageSent += SendMessage;
            };
            _dashBoard.MessageSent += SendMessage;
        }

        public FluxoContextFactory ContextFactory
        {
            get { return _contextFactory; }
            set
            {
                if (Equals(value, _contextFactory)) return;
                _contextFactory = value;
                OnPropertyChanged("ContextFactory");
            }
        }

        public AutenticadorDeSessao LoginAuthenticator
        {
            get { return _loginAuthenticator; }
            set
            {
                if (Equals(value, _loginAuthenticator)) return;
                _loginAuthenticator = value;
                OnPropertyChanged("LoginAuthenticator");
            }
        }

        public ICommand Logar
        {
            get
            {

                return new FunctionalCommand(() =>
                {
                    var lw = new LoginWindow();
                    lw.ShowDialog();

                }, () => LoginAuthenticator.CurrentSession == null);
            }
        }

        public ICommand Sair
        {
            get
            {
                return new FunctionalCommand(() => LoginAuthenticator.Logoff(),
                    () => LoginAuthenticator.CurrentSession != null);
            }
        }

        public DashBoardViewModel DashBoard
        {
            get { return _dashBoard; }
            set
            {
                if (Equals(value, _dashBoard)) return;
                _dashBoard = value;
                OnPropertyChanged("DashBoard");
            }
        }
        
        private ICommand AbrirListaGenericaCommand<T>(Action<Action<IActionResult>> customInserir = null
            , Action<object, Action<IActionResult>> customModificar = null
            , Action<object, IDbContext> customDelete = null
            , Func<IDbSet,IDbContext, IQueryable > customFiltrar = null)
        {
            return new FunctionalCommand(() =>
            {
               
                var model = new EntityListViewerModel(() => ContextFactory.Create(), typeof (T),
                
                    customInserir: customInserir,
                    customModificar: customModificar,
                    customDelete: customDelete,
                    customFiltrar: customFiltrar
                );
                var wnd = new EntityListViewerWindow(model);                
                wnd.Show();

            }, () => LoginAuthenticator.CurrentSession != null);
        }

        public ICommand CadastrarContaFinanceiraCommand
        {
            get { return AbrirListaGenericaCommand<ContaFinanceira>(); }
        }

        public ICommand CadastrarBancoCommand
        {
            get { return AbrirListaGenericaCommand<Banco>(); }
        }

        public ICommand CadastrarContaFinanceiraBancariaCommand
        {
            get { return AbrirListaGenericaCommand<ContaFinanceiraBancaria>(); }
        }

        public ICommand CadastrarTipoDeContaBancariaCommand
        {
            get { return AbrirListaGenericaCommand<TipoDeContaBancaria>(); }
        }

        public ICommand CadastrarCategoriaLancamentoCommand
        {
            get { return AbrirListaGenericaCommand<Categoria>(); }
        }
        public ICommand CadastrarFeriadoVariavelLancamentoCommand
        {
            get { return AbrirListaGenericaCommand<FeriadoDataVariavel>(); }
        }
        public ICommand CadastrarFeriadoFixoLancamentoCommand
        {
            get { return AbrirListaGenericaCommand<FeriadoDataFixa>(); }
        }
        public ICommand CadastrarLancamentoCommand
        {
            get
            {
                return AbrirListaGenericaCommand<Lancamento>(action =>
                {
                    LancamentoCadastroWindow.Inserir(action);
                },
                (o, action) =>
                {
                    var lanc = (Lancamento) o;
                    if (lanc.Movimentos.All(m => m.Conta is ContaCredito))
                    {
                        var vm = new LancamentoCartaoCreditoWindowViewModel();
                        vm.SetAction(FluxoContextFactory.GetInstance().Create(), CRUDOperationType.Update, lanc);
                        var telaLancamentoCartaoCredito = new LancamentoCartaoDeCreditoWindow(vm);
                        telaLancamentoCartaoCredito.Show();
                    }
                    else
                    {
                        LancamentoCadastroWindow.Modificar(lanc, action);  
                    }
                    
                },(o, ctx) =>
                {
                    var entity = (Lancamento) o;

                    foreach (var lr in ctx.Set<LancamentoRecorrenteVencimento>()
                        .Where(l=> l.LancamentoId == entity.LancamentoId).ToArray())
                    {
                        lr.LancamentoId = null;
                    }

                    ctx.Set<Lancamento>().Remove(entity);
                    ctx.SaveChanges();
                }, (set,ctx) =>
                {
                    var idCatTrans = ((FluxoDeCaixaContextFiltrado) ctx).GetCategoriaTransferencia().CategoriaId;
                    return set.Project().Cast<Lancamento>().Where(la => la.Categoria.CategoriaId != idCatTrans).AsQueryable();

                });
            }
        }

        public ICommand CadastrarLancamentoRecorrenteCommand
        {
            get
            {
                return AbrirListaGenericaCommand<LancamentoRecorrente>(LancamentoRecorrenteCadastroWindow.Inserir,
                    (o, action) => LancamentoRecorrenteCadastroWindow.Modificar((LancamentoRecorrente)o, action));
            }
        }

        public ICommand CadastrarContaCreditoCommand
        {
            get { return AbrirListaGenericaCommand<ContaCredito>(); }
        }

        public ICommand CadastrarBandeiraCommand
        {
            get { return AbrirListaGenericaCommand<BandeiraCredito>(); }
        }

        public ICommand CadastrarEmprestimoCommand
        {
            get
            {
                return AbrirListaGenericaCommand<Emprestimo>(EmprestimoCadastroWindow.Inserir,
                    (o, action) => EmprestimoCadastroWindow.Modificar((Emprestimo) o, action)
                    , (o, ctx) =>
                {
                    var entity = (Emprestimo) o;

                    ctx.Set<Lancamento>().Remove(entity.Saida);
                    if (entity.Pagamento != null)
                    {
                        ctx.Set<Lancamento>().Remove(entity.Pagamento);
                    }
                    ctx.Set<Emprestimo>().Remove(entity);
                    
                    ctx.SaveChanges();
                });
            }
        }

        public ICommand LancamentoCompraCreditoCommand
        {
            get
            {
                return new FunctionalCommand(() =>
                {
                    var vm = new LancamentoCartaoCreditoWindowViewModel();
                    vm.SetAction(FluxoContextFactory.GetInstance().Create(), CRUDOperationType.Create);
                    var telaLancamentoCartaoCredito = new LancamentoCartaoDeCreditoWindow(vm);
                    telaLancamentoCartaoCredito.Show();
                });

            }
        }
        public ICommand TransferiaEntreContasCommand
        {
            get
            {
                return AbrirListaGenericaCommand<TransferenciaEntreContas>(TransferenciaEntreContasWindow.Inserir,
                    (o, action) => TransferenciaEntreContasWindow.Modificar((TransferenciaEntreContas)o, action), 
                    (o, ctx) =>
                    {
                        var entity = (TransferenciaEntreContas)o;

                        ctx.Set<Lancamento>().Remove(entity.LancamentoSaida);
                        ctx.Set<Lancamento>().Remove(entity.LancamentoEntrada);
                        ctx.Set<TransferenciaEntreContas>().Remove(entity);

                        ctx.SaveChanges();
                    });

            }
        }
        public ICommand CadastrarPessoaCommand
        {
            get
            {
                return AbrirListaGenericaCommand<Pessoa>();
            }
        }
        public ICommand FaturaCompraCreditoCommand
        {
            get
            {
                return new FunctionalCommand(() =>
                {

                    var model = new EntityListViewerModel(() => ContextFactory.Create(), typeof(FaturaCredito),
                        customDelete: (o, ctx) =>
                        {
                            var entity = (FaturaCredito)o;

                            var transf = ctx.Set<TransferenciaEntreContas>()
                                .SingleOrDefault(t => t.LancamentoEntrada.LancamentoId == entity.LancamentoPagamento.LancamentoId
                                || t.LancamentoSaida.LancamentoId == entity.LancamentoPagamento.LancamentoId);
                            if (transf != null)
                            {
                                ctx.Set<Lancamento>().Remove(transf.LancamentoEntrada);
                                ctx.Set<Lancamento>().Remove(transf.LancamentoSaida);
                                ctx.Set<TransferenciaEntreContas>().Remove(transf);
                            }

                            if (entity.LancamentoPagamento != null) 
                                ctx.Set<Lancamento>().Remove(entity.LancamentoPagamento);

                            ctx.Set<FaturaCredito>().Remove(entity);

                            ctx.SaveChanges();
                            
                            SendMessage("Fatura excluída com sucesso");
                        }
                    );
                    model.IncluirVisivel = false;
                    model.ModificarVisivel = false;
                    var wnd = new EntityListViewerWindow(model);
                    
                    wnd.Show();

                }, () => LoginAuthenticator.CurrentSession != null);
              
            }
        }
        public ICommand GerenciadorCartaoCreditoCommand
        {
            get
            {
                return new FunctionalCommand(() =>
                {
                    var tela = new GerenciadorDeCartaoCreditoWindow();
                    tela.Show();
                });
            }
        }

        public ICommand TrocarSenhaCommand
        {
            get { return new FunctionalCommand(() => new TrocarSenhaindow().Show(), ()=> LoginAuthenticator.UsuarioEstaLogado); }
        }
    }

    public class ViewModelLocator
    {
        private Dictionary<Type, Func<object>> _registeredCreators;

        public ViewModelLocator()
        {
            _registeredCreators = new Dictionary<Type, Func<object>>();
        }

        public void RegisterService<T>(Func<T> creator)
        {
            _registeredCreators[typeof (T)] = ()=> creator();
        }

        public T GetService<T>()
        {
            return (T)_registeredCreators[typeof (T)]();
        }
    }
}
